package com.sinsz.c.aop;

import com.sinsz.c.context.BxfsLoggerUtil;
import com.sinsz.c.context.support.LoggerEnum;
import com.sinsz.c.exception.IError;
import com.sinsz.c.exception.SystemError;
import com.sinsz.c.exception.SystemException;
import com.sinsz.c.result.Result;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 异常拦截器
 * @author chenjianbo
 */
@Component
@Aspect
@AutoConfigureOrder(0)
public class ThrowableAop {

    private static final Logger logger = LoggerFactory.getLogger(ThrowableAop.class);

    @Autowired
    private HttpServletRequest request;

    /**
     * 拦截切入点
     */
    @Pointcut("execution(* *..controller.*Controller.*(..))")
    public void web(){}

    /**
     * 1、rest满足切入条件
     */
    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
    public void rest() {}

    /**
     * 2.1、传统方式类注解满足切入条件
     */
    @Pointcut("@within(org.springframework.stereotype.Controller)")
    public void controller() {}

    /**
     * 2.2、传统方式方法注解满足切入条件
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.ResponseBody)")
    public void responseBody() {}

    @Around("(web() && rest()) " +
            "|| (web() && controller() && responseBody() )")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object retVal;
        long starterTime = System.currentTimeMillis();
        //获取信息
        String remoteHost = request.getRemoteHost();
        String requestUri = request.getRequestURI();
        String method = joinPoint.getSignature().toShortString();
        Map<String, String[]> requestParams = request.getParameterMap();
        Map<String, String> requestHeaders = new HashMap<>(1);
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            String value = request.getHeader(name);
            requestHeaders.put(name, value);
        }
        try {
            retVal= joinPoint.proceed();
            if (logger.isDebugEnabled()) {
                logger.debug("地址:{} ; 路径:{} ; 执行方法:{} ; 耗时:{} 毫秒.",
                        remoteHost, requestUri, method, System.currentTimeMillis() - starterTime);
            }
            BxfsLoggerUtil.instance().simpleInputLogger(
                    LoggerEnum.NORMAL,
                    remoteHost,
                    requestUri,
                    method,
                    requestParams,
                    requestHeaders,
                    null
            );
        } catch (Throwable throwable) {
            IError error;
            if (throwable instanceof SystemException) {
                SystemException ae = (SystemException) throwable;
                error = ae.getError();
            } else if (throwable.getCause() instanceof SQLException) {
                error = SystemError.SYSTEM_SQL_ERR;
            } else {
                error = SystemError.SYSTEM_ERR;
            }
            retVal = Result.fail(error, throwable);
            if (logger.isErrorEnabled()) {
                logger.error("地址:{} ; 路径:{} ; 执行方法:{} ; 耗时:{} 毫秒. 异常消息: {}",
                        remoteHost, requestUri, method, System.currentTimeMillis() - starterTime,
                        throwable.getMessage());
            }
            BxfsLoggerUtil.instance().simpleInputLogger(
                    LoggerEnum.ERROR,
                    remoteHost,
                    requestUri,
                    method,
                    requestParams,
                    requestHeaders,
                    throwable.getLocalizedMessage()
            );
            if (logger.isDebugEnabled()) {
                throwable.printStackTrace(System.out);
            }
        }
        return retVal;
    }

}